import 'dart:async';

import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:yeild_libs_dal/dal.dart';
import 'package:go_router/go_router.dart';

part 'main.g.dart';

/// Example of a custom DAL repository for application
class AppDalScope extends DalScope {
  /// The current instance of application.
  static AppDalScope? _current;

  /// The CounterRepository instance for this application in this scope.
  CounterRepository? _counterRepository;

  AppDalScope._() : super();

  /// Factory constructor to create an AppDalScope instance
  factory AppDalScope.of() {
    // Create a new instance of AppDalScope and initialize it.
    _current ??= AppDalScope._()..onCreate();
    return _current!;
  }

  @override
  void onCreate() {
    super.onCreate();
    // Initialize the repositories here.
    _counterRepository = CounterRepository.of(this);
  }
}

extension AppDalScopeExtension on DalScope {
  /// Load the existing lower scope or create a new one.
  LowerScope loadLowerScope() {
    return scope() ?? LowerScope.of(this);
  }
}

/// Example of a custom DAL scope for managing lower-level resources
class LowerScope extends DalScope {
  /// The CounterRepository instance for this application in this scope.
  CounterRepository? _counterRepository;

  LowerScope._(super.scope);

  /// Factory constructor to create a LowerScope instance
  factory LowerScope.of(DalScope scope) {
    return LowerScope._(scope)..onCreate();
  }

  @override
  void registerSelf() {
    parent?.registerScope(this);
  }

  @override
  void onCreate() {
    super.onCreate();
    // Initialize the repositories here.
    _counterRepository = CounterRepository.of(this);
  }
}

/// Example of a custom DAL repository for managing a counter
class CounterRepository extends DalRepository {
  CounterRepository._(super.scope);

  factory CounterRepository.of(DalScope scope) {
    return CounterRepository._(scope)..onCreate();
  }

  final ValueNotifier<int> counter = ValueNotifier(0);

  @override
  void registerSelf() {
    scope.registerRepo<CounterRepository>(this);
  }

  @override
  void onCreate() {
    super.onCreate();
    // Initialize the repository
  }

  @override
  Future<bool> onReset() async {
    // Perform any initialization logic here
    counter.value = 0;
    return super.onReset();
  }

  @override
  void onDisposed() {
    // Clean up resources when the repository is disposed
    counter.dispose();
    super.onDisposed();
  }

  void increment() {
    counter.value++;
    notifyListeners();
  }
}

/// Example of a custom DAL view model for managing a counter
class CounterViewModel extends DalViewModel {
  /// The CounterRepository instance for this view model
  CounterRepository? _repository;
  final ValueNotifier<int> counter = ValueNotifier(0);

  CounterViewModel(super.scope);

  factory CounterViewModel.of(DalScope scope) {
    return CounterViewModel(scope)..onInit();
  }

  void onInit() {
    print(
      'ViewModel@${identityHashCode(this)} initialized with scope: ${scope.runtimeType}',
    );
    // Watch for changes of the CounterRepository in the scope
    autoWatchRepo(bindCounterRepository);
  }

  @override
  void dispose() {
    print(
      'ViewModel@${identityHashCode(this)} dispose with scope: ${scope.runtimeType}',
    );
    super.dispose();
    counter.dispose();
  }

  /// Callback to handle changes in the CounterRepository
  void bindCounterRepository(CounterRepository? repository) {
    _repository = repository;
    // Update the counter value when the repository changes
    autoWatchValue(_repository?.counter, onCounterChanged);
  }

  /// Callback to handle changes in the counter value
  void onCounterChanged(int? value) {
    print(
      'ViewModel@${identityHashCode(this)} with scope: ${scope.runtimeType} onCounterChanged: $value',
    );
    // Update the counter value when the counter value of repository changes
    counter.value = value ?? 0;
    // Reset the counter value when it exceeds 50
    if (counter.value > 50) _repository?.onReset();
  }

  void increment() {
    _repository?.increment();
  }
}

void main() {
  runApp(
    MultiProvider(
      providers: [
        // Initialize the AppDalScope
        ChangeNotifierProvider(
          create: (context) => AppDalScope.of() as DalScope,
        ),
      ],
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  final GoRouter _router = GoRouter(
    routes: $appRoutes,
    initialLocation: '/home',
    debugLogDiagnostics: true,
  );

  MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
      future: context.read<DalScope>().init(),
      builder: (context, snapshot) {
        if (snapshot.connectionState == ConnectionState.done) {
          return MaterialApp.router(
            routerConfig: _router,
            title: 'DAL Demo',
            theme: ThemeData(
              colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
            ),
          );
        } else {
          // Show a loading indicator while waiting for initialization
          return const Center(child: CircularProgressIndicator());
        }
      },
    );
  }
}

@TypedStatefulShellRoute<RouteHomeShell>(
  branches: <TypedStatefulShellBranch<StatefulShellBranchData>>[
    TypedStatefulShellBranch<RouteHomeBranchMain>(
      routes: <TypedRoute<RouteData>>[
        TypedGoRoute<RouteHomeMain>(
          path: '/home',
          routes: <TypedRoute<RouteData>>[
            TypedGoRoute<RouteDialogShowMain>(path: 'show'),
          ],
        ),
      ],
    ),
    TypedStatefulShellBranch<RouteHomeBranchUser>(
      routes: <TypedRoute<RouteData>>[
        // 初学者，暂时只发现通过嵌套一层TypedStatefulShellRoute实现对/user及其所有子路由context统一隔离
        TypedStatefulShellRoute<RouteHomeUserShell>(
          branches: <TypedStatefulShellBranch<StatefulShellBranchData>>[
            TypedStatefulShellBranch<RouteHomeUserBranch>(
              routes: <TypedRoute<RouteData>>[
                TypedGoRoute<RouteUserHome>(
                  path: '/user',
                  routes: <TypedRoute<RouteData>>[
                    TypedGoRoute<RouteDialogShowUser>(path: 'show'),
                  ],
                ),
              ],
            ),
          ],
        ),
      ],
    ),
  ],
)
class RouteHomeShell extends StatefulShellRouteData {
  const RouteHomeShell();

  static String $restorationScopeId = 'RouteHomeShell';

  @override
  Widget builder(
    BuildContext context,
    GoRouterState state,
    StatefulNavigationShell navigationShell,
  ) {
    return HomeShellScreen(navigationShell: navigationShell);
  }
}

class RouteHomeBranchMain extends StatefulShellBranchData {
  const RouteHomeBranchMain();
  static String $restorationScopeId = 'RouteHomeBranchMain';
}

class RouteHomeMain extends GoRouteData with _$RouteHomeMain {
  const RouteHomeMain();

  @override
  Page<void> buildPage(BuildContext context, GoRouterState state) {
    return MaterialPage<void>(
      key: state.pageKey,
      child: const HomeMainScreen(),
    );
  }
}

class RouteHomeBranchUser extends StatefulShellBranchData {
  const RouteHomeBranchUser();
  static String $restorationScopeId = 'RouteHomeBranchUser';
}

class RouteHomeUserShell extends StatefulShellRouteData {
  const RouteHomeUserShell();

  static String $restorationScopeId = 'RouteHomeUserShell';

  @override
  Widget builder(
    BuildContext context,
    GoRouterState state,
    StatefulNavigationShell navigationShell,
  ) {
    return HomeUserShellScreen(navigationShell: navigationShell);
  }
}

class RouteHomeUserBranch extends StatefulShellBranchData {
  const RouteHomeUserBranch();
  static String $restorationScopeId = 'RouteHomeUserBranch';
}

class RouteUserHome extends GoRouteData with _$RouteUserHome {
  const RouteUserHome();
  static String $restorationScopeId = 'RouteUserHome';

  @override
  Page<void> buildPage(BuildContext context, GoRouterState state) {
    return MaterialPage<void>(
      key: state.pageKey,
      child: const UserHomeScreen(),
    );
  }
}

class RouteDialogShow extends GoRouteData {
  const RouteDialogShow();

  @override
  Page<void> buildPage(BuildContext context, GoRouterState state) {
    CounterRepository? repository = context.read<DalScope>().repo();
    return DialogPage(
      key: state.pageKey,
      child: Center(
        child: SizedBox(
          width: 300,
          height: 300,
          child: Card(
            child: Center(
              child: Text('Count is: ${repository?.counter.value}'),
            ),
          ),
        ),
      ),
    );
  }
}

class RouteDialogShowMain extends RouteDialogShow with _$RouteDialogShowMain {
  const RouteDialogShowMain();
}

class RouteDialogShowUser extends RouteDialogShow with _$RouteDialogShowUser {
  const RouteDialogShowUser();
}

class HomeShellScreen extends StatelessWidget {
  const HomeShellScreen({required this.navigationShell, super.key});

  final StatefulNavigationShell navigationShell;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: navigationShell,
      bottomNavigationBar: BottomNavigationBar(
        items: const <BottomNavigationBarItem>[
          BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Main'),
          BottomNavigationBarItem(icon: Icon(Icons.group), label: 'User'),
        ],
        currentIndex: navigationShell.currentIndex,
        onTap: (int index) => _onTap(context, index),
      ),
    );
  }

  void _onTap(BuildContext context, int index) {
    navigationShell.goBranch(
      index,
      initialLocation: index == navigationShell.currentIndex,
    );
  }
}

class HomeMainScreen extends StatelessWidget {
  final String title;

  const HomeMainScreen({this.title = 'Main Home Page', super.key});

  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
      future: context.read<DalScope>().init(),
      builder: (context, snapshot) {
        if (snapshot.connectionState == ConnectionState.done) {
          return MyHomePage(
            title: title,
            // Provide the CounterViewModel to the page
            viewModel: CounterViewModel.of(context.read()),
            isUserPage: this is UserHomeScreen,
          );
        } else {
          // Show a loading indicator while waiting for initialization
          return const Center(child: CircularProgressIndicator());
        }
      },
    );
  }
}

class HomeUserShellScreen extends StatelessWidget {
  const HomeUserShellScreen({required this.navigationShell, super.key});

  final StatefulNavigationShell navigationShell;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: MultiProvider(
        providers: [
          ChangeNotifierProvider.value(
            value: context.read<DalScope>().loadLowerScope() as DalScope,
          ),
        ],
        child: navigationShell,
      ),
    );
  }
}

class UserHomeScreen extends HomeMainScreen {
  const UserHomeScreen({super.key}) : super(title: 'User Home Page');
}

class DialogPage extends Page<void> {
  /// A page to display a dialog.
  const DialogPage({required this.child, super.key});

  /// The widget to be displayed which is usually a [Dialog] widget.
  final Widget child;

  @override
  Route<void> createRoute(BuildContext context) {
    return DialogRoute<void>(
      context: context,
      settings: this,
      builder: (BuildContext context) => child,
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({
    super.key,
    required this.title,
    required this.viewModel,
    this.isUserPage = false,
  });

  final String title;
  final bool isUserPage;

  /// The view model for managing the counter
  final CounterViewModel viewModel;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  void dispose() {
    widget.viewModel.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Center(
        child: InkWell(
          onTap: () {
            if (widget.isUserPage) {
              RouteDialogShowUser().go(context);
            } else {
              RouteDialogShowMain().go(context);
            }
          },
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              const Text(
                'You have pushed the button this many times within app scope:',
              ),
              ListenableBuilder(
                listenable: widget.viewModel.counter,
                builder: (context, child) {
                  return Text(
                    '${widget.viewModel.counter.value}',
                    style: Theme.of(context).textTheme.headlineMedium,
                  );
                },
              ),
            ],
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => widget.viewModel.increment(),
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}
